### Version History

### Integration v.01 changes:
### adding the player class and displaying the player object on the screen

###Restructure v.02 changes:
### Testing key events within the main game loop
### Adding transition from game loop back to shell loop

### Restructure v.01 changes
### Restructure Code as per program structure file

### v.09 changes
### separation of textobjects into a separate class
### creation of menu function to handle display of menu items

### v.08 changes
### add game over screen which displays final score, and asks the player whether to start again, or quit

### v.07 changes:
### add score and lives variables to the test function
### test the transition from the game to the game over screen

### v.06 changes:
### define a function to show the test display, and start the function when start is selected and 'enter' is pressed. 
### This test function is a placeholder for the main game function running inside the main shell loop

### v.05 changes:
### create test display to test starting the game

### v.04 changes:
### Test key events to switch selection and test quitting the game

### v.03 changes:
### introduce menumode and selected variables
### define key events to switch selection if in menumode

###  v.02 changes:
### changes text displayed on screen after a short pause
### creates new main class to initialise the game


###1. Import modules:

import pygame
from pygame.locals import *
import sys
import os

### 2: Declare public variables
### these variables need to be accessible to various functions in various classes


screenWidth = 800
screenHeight = 600
screenSize = screenWidth, screenHeight

"""menumode keeps track of the gamestate - allowed values are: -2 - opening screen is displayed, no menu available, 0 - control map is displayed, 1 - start menu displayed, -1 - main game loop running, no menu displayed, 2 - restart menu displayed"""
menumode = -2 

"""the selected variable keeps track of which menuitem the player has selected. It can have the following values: 0 - no selectable item displayed,  1 - first/upper menuitem selected; -1 - lower/second menuitem selected"""
selected = 0

score = 0
finalScore = 0



enemyMinx = 0
enemyMaxX = screenWidth
enemyDown = 32
moveSpeed = 3


### 3. Define classes

class BugsMain:

	def __init__(self): 

    		##initialise pygame
    		pygame.init()

	
   	 	## create the display
    		self.screen = pygame.display.set_mode(screenSize)
		pygame.display.set_caption("Bugs!")

    		self.background = pygame.Surface(self.screen.get_size()) #ensures background is the same size as the game window
 		self.background = self.background.convert()
   		self.background.fill((0, 0, 0))

    		## make the background visible and refresh
		self.screen.blit(self.background, (0, 0))
	      	pygame.display.flip()

	def MenuKeyMap(self):
		"""This function displays the keymap to the player and waits for any key to be pressed before transition to the start/quit menu"""

		global menumode
		global selected

		self.screen.blit(self.background, (0,0))
		pygame.display.flip()


		##initialise text objects 

		textControls = BugsText(None, 64)
		textControls.write("Controls", (0, 0, 255))
		textControls.adjustPos(0, -230)
		
		textKeyNav = BugsText(None, 36)
		textKeyNav.write("Up and Down arrows - navigate menu", (164, 164, 164))
		textKeyNav.adjustPos(0, -165)

		textEnter = BugsText(None, 36)
		textEnter.write("Enter - confirm selection", (164, 164, 164))
		textEnter.adjustPos(0, -90)

		textQuit = BugsText(None, 36)
		textQuit.write("Esc - quit", (164, 164, 164))
		textQuit.adjustPos(0, -15)			
		
		textMove = BugsText(None, 36)
		textMove.write("Left and Right arrows - move player", (164, 164, 164))
		textMove.adjustPos(0, 60)

		textShoot = BugsText(None, 36)
		textShoot.write("Spacebar - fire missile", (164, 164, 164))
		textShoot.adjustPos(0, 135)

		textContinue = BugsText(None, 24)
		textContinue.write("Press any key to continue", (128, 128, 128))
		textContinue.adjustPos(0, 200)


		## display text objects

		self.screen.blit(textControls.message, textControls.pos)
		self.screen.blit(textKeyNav.message, textKeyNav.pos)
		self.screen.blit(textEnter.message, textEnter.pos)
		self.screen.blit(textQuit.message, textQuit.pos)
		self.screen.blit(textMove.message, textMove.pos)
		self.screen.blit(textShoot.message, textShoot.pos)
		self.screen.blit(textContinue.message, textContinue.pos)
		pygame.display.flip()

		"""This function waits for input from the player once all text objects are displayed"""

		



		



	def MenuStart(self):
		"""This function displays the Start/Quit menu with 'Start selected and modifies the 'selected' variable accordingly"""

		global selected

		self.screen.blit(self.background, (0,0))
		pygame.display.flip()
		
		textStartS = BugsText(None, 48)
		textStartS.write("START", (255, 64, 64))
		textStartS.adjustPos(0, -50)

		textQuit = BugsText(None, 48)
		textQuit.write("QUIT",  (166, 166, 166))
		textQuit.adjustPos(0, 50)
			
		self.screen.blit(textStartS.message, textStartS.pos)
		self.screen.blit(textQuit.message, textQuit.pos)
		pygame.display.flip()

		
		selected = 1
		return

		"""This function waits for input from the player once text objects are displayed"""

		


	def MenuStartQSelect(self):
		"""This function is called when the selection in the start/quit menu is reversed. 'Quit' is now displayed as selected, and the 'selected' variable modified accordingly"""

		global selected

		self.screen.blit(self.background, (0,0))
		pygame.display.flip()
		
		textStart = BugsText(None, 48)
		textStart.write("START", (166, 166, 166))
		textStart.adjustPos(0, -50)

		textQuitS = BugsText(None, 48)
		textQuitS.write("QUIT",  (255, 64, 64))
		textQuitS.adjustPos(0, 50)
			
		self.screen.blit(textStart.message, textStart.pos)
		self.screen.blit(textQuitS.message, textQuitS.pos)
		pygame.display.flip()

		selected = -1
		return

		"""This function waits for input from the player once text objects are displayed"""


	def MenuGameOver(self):
		"""This function deals with the transition from the MainGame loop back to the shell loop. It displays the players final scor and asks if they want to play again."""
	
		global menumode
		global score
		global selected

		finalScore = getScore(self)

		self.screen.fill((0, 0, 0))
		pygame.display.flip()

		textGameOver = BugsText(None, 128)
		textGameOver.write("GAME OVER!", (255, 0, 0))
		textGameOver.adjustPos(0, 0)
		self.screen.blit(textGameOver.message, textGameOver.pos)
		pygame.display.flip()

		pygame.time.delay(2500)

		self.screen.blit(self.background, (0, 0))
		pygame.display.flip()


		## get the last value of score and display it as final score
		textFinalScore = BugsText(None, 56)
		textFinalScore.write("Your final score is: " + finalScore, (0,255,0))
		textFinalScore.adjustPos(0, -75)
		
		textPlayAgain = BugsText(None, 48)
		textPlayAgain.write("Select 'Start' to play again or 'Quit' to exit", (164, 164, 164))
		textPlayAgain.adjustPos (0, 50)
		
		self.screen.blit(textFinalScore.message, textFinalScore.pos)
		self.screen.blit(textPlayAgain.message, textPlayAgain.pos)
		pygame.display.flip()

		pygame.time.delay(5000)
		menumode = 1
		selected = 1

		MainWindow.MenuStart()	
		

			

	def MainGameLoop(self):
		"""This is where the game will run inside the shell loop"""

		global menumode
		global score

		score = 0
		lives = 3

		scoreDisplay = getScore(self)

		### initialise new background
		bg = os.path.join("image", "space_bg.png")
		self.background2 = pygame.image.load(bg).convert()


		### initialise hostiles

		enemyMinx = 0
		enemyMaxX = screenWidth
		enemyDown = 32
		moveSpeed = 3

		enemy1 = Enemy([0,0])

		enemy1 = pygame.sprite.RenderUpdates()

		for position in [
        		([2,50]),
			([50, 50]),
        		([100,50]),
        		([150,50]),
        		([200,50]),
        		([250,50]),
        		([2,114]),
        		([50,114]),
        		([100,114]),
        		([150,114]),
        		([200,114]),
        		([250,114])
        		
        		]:

        		enemy1.add(Enemy(position))


		

		self.screen.blit(self.background2, (0,0))
		pygame.display.flip()
		
		

		textScore = BugsText(None, 24)
		textScore.write("Score: " + scoreDisplay, (0, 0, 255))
		textScore.adjustPos(350, -265)

		
		##self.screen.blit(textScore.message, textScore.pos) - to be handled by update function
		##pygame.display.flip()

		##wrapping condition - game runs until the player runs out of lives

		while lives > 0:


			enemy1.update(pygame.time.get_ticks(), enemyMinx,enemyMaxX,enemyDown, moveSpeed) 
  
        		rectlist = enemy1.draw(self.screen)
			self.screen.blit(textScore.message, textScore.pos)
			pygame.display.flip()

			
			



        		pygame.time.delay(75)

        		enemy1.clear(self.screen, self.background2)

			for event in pygame.event.get():
				if event.type == QUIT:
					sys.exit()

				elif event.type == KEYDOWN:

					if event.key == K_ESCAPE: #player chooses to exit the game
						sys.exit()


			


		### end of placeholder

		"""Once the variable 'lives' has reached 0 this function changes the value of menumode and uses the MenuGameOver function to display the gameover screen"""
		
		menumode = 2
		MainWindow.MenuGameOver()




	

		


		
		

		


	def ShellLoop(self):

		"""This is where all the events in the game will run"""

		global menumode 
		global selected 

		### initialise opening display
		textWelcome = BugsText(None, 48)
		textWelcome.write("Splat the bugs and win fabulous prizes!", (255, 0, 0))
		textWelcome.adjustPos(0, 0)
		
		self.screen.blit(textWelcome.message, textWelcome.pos)
    		pygame.display.flip()

		pygame.time.delay(2000)

		menumode = 0
		MainWindow.MenuKeyMap()

		
		


		while 1:
			for event in pygame.event.get():
				if event.type == QUIT:
					return

				elif event.type == KEYDOWN:

					if menumode == 0: ## pressing any key during this stage triggers MenuStart
						menumode = 1
						MainWindow.MenuStart()

					elif event.key == K_ESCAPE: #player chooses to exit the game
						return

					elif menumode == 1: #starting menu is displayed
						if event.key == K_UP:
							if selected == -1: # 'quit' is currently selected,
								MainWindow.MenuStart()
						
            
						if event.key == K_DOWN:
							if selected == 1: # 'start' is currently selected
								MainWindow.MenuStartQSelect()


						if event.key == K_RETURN:
              						if selected == -1:   ## if 'quit' is selected when return key pressed, game exits
								sys.exit("bye-bye!")

							elif selected == 1: ## if 'start' is selected when return key pressed, main game starts
								selected = 0 #reset selected variable
								menumode = -1 #set menumode to indicate that the main game is now running                
								MainWindow.MainGameLoop()


					


		
		


class BugsText(pygame.font.Font):
  	"""This is the class that will handle text objects and their attributes"""  

  	def __init__(self, font_name, size):
    		pygame.font.Font.__init__(self, None, size)

	def write(self, message, color):
    		self.message = self.render(message, 1, color)
		self.pos = self.message.get_rect()
   

	def adjustPos(self, adjustX, adjustY):
		self.pos.centerx = screenWidth/2 + adjustX
		self.pos.centery = screenHeight/2 +adjustY
 
  	



### Sprite class goes here

### Player class goes here

class Player(pygame.sprite.Sprite):
	pos=[400,560]
	missileMoving=False
	missileY=pos[1]
##______________________ ##

    	def __init__(self):
        	pygame.sprite.Sprite.__init__(self) #initialise the parent class

#______________________##

    	def loadImage(self,initialPosition):
        	self.image=pygame.image.load("hero.jpg").convert()
        	self.imagerect=self.image.get_rect() # get the rectangle of the image
        	self.imagerect.topleft=initialPosition # set the top left corner to be equal

#_____________________________#
    	def moveLeft(self):
       		self.pos[0]-=5        #move  5 pixels to the left
       		self.imagerect.topleft=(self.pos[0],self.pos[1]) #use topleft as reference
       
       		#print self.pos[0]     #see down for this function
       		
		if self.imagerect.topleft==(0,self.pos[1]):       #if the hero reaches the border of the screen
           		self.pos[0]=self.pos[0]+5                           #the last movement is ignored because five pixels are added
      
#_______________________________#  same as above function
    	def moveRight(self):                                                
       		self.pos[0]+=5
       		self.imagerect.topright=(self.pos[0]+32,self.pos[1]) #using the top right corner of the image which is situated 32 pixels after the
                                       
       		#print self.pos[0]
       		#print self.imagerect.topright[0]

       		if self.imagerect.topright[0]>=800:                       # use the corner as a stopper preventing the image from moving
           		self.pos[0]=self.pos[0]-5






### Missile class goes here

### Enemy class goes here

class Enemy(pygame.sprite.Sprite):
        """
        This class can be used to create an enemy object. Functions and notes are as follows:
        
        __init__
                defines initial variables used within the class
        
        loadimages
                loads each image for the enemy animation
                an error message will be brought up if correct images are not found
        
        update
                use RenderUpdates()
                updates animation frame each time update is called
                takes the following parameters
                        currentTime     
                                obtained from pygame.time.get_ticks()
                        enemyMinx
                                obtained from level class
                        enemyMaxX
                                obtained from level class
                        enemyDown
                                obtained from level class
                        moveSpeed
                                obtained from level class
        move
                controls the enemy path
                takes the following parameters
                        left
                                obtained from update function via enemyMinx
                        right
                                obtained from update function via enemyMaxX
                        down
                                obtained from update function via enemyDown
                        speed
                                obtained from update function via moveSpeed
        """
        image = None
	global enemyMinx
	global enemyMaxX
	global enemyDown 
	global moveSpeed
        
        #########################################
        #Set inital variables
        #########################################
        
        def __init__(self, initialPosition):
                pygame.sprite.Sprite.__init__(self)
        
                
                
                if Enemy.image is None:
                        self.loadImages()
            # This is the first time this class has been instantiated.
            # So, load the image for this and all subsequence instances
                        self.image = self.temp
                        self.rect = self.image.get_rect()
                        self.rect.topleft = initialPosition
                        self.frame = 0
                        self.nextUpdateTime = 0
                        self.goingRight =  True
                        self.goingLeft =  False
                        self.goingDownLeft = False
                        self.goingDownRight = False
                        self.downTempL = 0
                        self.downTempR = 0
                        

        #########################################
        #loadImages Function
        #Loads images for enemy animation
        #########################################
        
        def loadImages(self):
                self.enemy1 = []
                
                try:
                        #frame 1
                        a1f1 = os.path.join("image","a1f1.gif")
                        self.temp = pygame.image.load(a1f1).convert()
                        key = self.temp.get_at((1, 1)) #find pix and use as trans colour
                        self.temp.set_colorkey(key)
                
                        #add frame
                        self.enemy1.append(self.temp)
                
                        #frame 2
                        a1f2 = os.path.join("image","a1f2.gif")
                        self.temp = pygame.image.load(a1f2).convert()
                        key = self.temp.get_at((1, 1))
                        self.temp.set_colorkey(key)
                
                        #add frame
                        self.enemy1.append(self.temp)
                
                        #frame 3
                        a1f3 = os.path.join("image","a1f3.gif")
                        self.temp = pygame.image.load(a1f3).convert()
                        key = self.temp.get_at((1, 1))
                        self.temp.set_colorkey(key)
                
                except pygame.error:
                        #elaborate error message
                        self.Font = pygame.font.Font(None, 20)   
                        self.textOpen = self.Font.render("Images totally cant be found. Closing...", 1, (255, 255, 255))
                        self.textOpenPos = self.textOpen.get_rect()
                        self.textOpenPos.centerx = background.get_rect().centerx
                        self.textOpenPos.centery = background.get_rect().centery

                        screen.blit(self.textOpen, self.textOpenPos)  
                        pygame.display.update()
                        pygame.time.delay(2000)
                        sys.exit(0)
                
                #add frame
                self.enemy1.append(self.temp)
        
        #########################################
        #Update function. Used with RenderUpdates()
        #########################################
        
        def update(self, currentTime, enemyMinx, enemyMaxX, enemyDown, moveSpeed):      #pygame.time.get_ticks(), move function
                if self.nextUpdateTime < currentTime:
                        #moves to the next frame, resets if there are no more
                        self.frame += 1
                        if self.frame >= len(self.enemy1):
                                self.frame = 0
                        self.image = self.enemy1[self.frame]
                        self.move(enemyMinx, enemyMaxX, enemyDown, moveSpeed)

                self.nextUpdateTime = currentTime + 10
                
                
        #########################################
        #Move function
        #########################################
        
        def move(self, left, right, down, speed):
                
                for count in range(speed): #used to differ from animation speed

                        #########################################
                        #Which way to go
                        #########################################                       
                        
                        if self.rect.right == right - 1:
                                self.goingRight = False
                                self.goingLeft = False
                                self.goingDownRight = True
                                self.goingDownLeft = False
                                
                        if self.rect.left == left + 1:
                                self.goingRight = False
                                self.goingLeft = False
                                self.goingDownRight = False
                                self.goingDownLeft = True
        
                        if self.downTempL == down + 1:
                                self.downTempL = 0
                                self.goingRight = True
                                self.goingLeft = False
                                self.goingDownRight = False
                                self.goingDownLeft = False
                        
                        if self.downTempR == down + 1:
                                self.downTempR = 0
                                self.goingRight = False
                                self.goingLeft = True
                                self.goingDownRight = False
                                self.goingDownLeft = False
                                
                        #########################################
                        #Movement
                        #########################################
                        
                        if self.goingDownLeft:
                                self.rect.bottom += 1
                                self.downTempL += 1
                                
                                
                        if self.goingDownRight:
                                self.rect.bottom += 1
                                self.downTempR += 1

                        
                        if self.goingRight:
                                self.rect.left +=1
                        
                        if self.goingLeft:
                                self.rect.right -=1
                                

###  Shield class goes here



### 4. Define functions

def getScore(self):
	global score
	return(str(score))





	






### Start Main class


if __name__ == '__main__':
  MainWindow = BugsMain()
  MainWindow.ShellLoop()





   


